﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;


namespace ru.yourpictures.Core.Imaging.Filters.PhotoFilters
{
    /// <summary>
    /// This class is dedicated for drawing frame around the picture.
    /// As a parameter it receives 4 corners and 4 side line images or
    /// just will use BackColor and border width and border height.
    /// <remarks>
    /// If the left/right/top/bottom framing having thickness less than 
    /// border width/height, it will be expanded to fill border using 
    /// Resize algorythm.
    /// </remarks>
    /// </summary>
    [FilterImplementation(GdiPlusImplemented = true, LowLevelImplemented = false)]
    public abstract class Frame : FilterBase
    {
        #region public enum FramingTypes
        public enum FramingTypes
        {
            /// <summary>System.Drawing.Color - BackColor will be used as a frame color</summary>
            SimpleColor,
            /// <summary>Start color and end color have to be set</summary>
            Gradient,
            /// <summary>8 images for corners and frame sides have to be provided</summary>
            CustomImages
        }
        #endregion

        #region Private Fields
        private FramingTypes _frameType = FramingTypes.SimpleColor;
        private int _borderWidth = 0;
        private int _borderHeight = 0;
        private Bitmap _leftTopImage = null;
        private Bitmap _rightTopImage = null;
        private Bitmap _rightBottomImage = null;
        private Bitmap _leftBottomImage = null;
        private Bitmap _bottomImage = null;
        private Bitmap _topImage = null;
        private Bitmap _rightImage = null;
        private Bitmap _leftImage = null;
        private System.Drawing.Color _endColor = System.Drawing.Color.Empty;
        private System.Drawing.Color _startColor = System.Drawing.Color.Empty;
        private LinearGradientMode _gradientMode = LinearGradientMode.ForwardDiagonal;
        #endregion

        #region Public Fields
        /// <summary>
        /// Gradient mode. Used when frame type is Gradient
        /// </summary>
        public LinearGradientMode GradientMode
        {
            get { return _gradientMode; }
            set { _gradientMode = value; }
        }
        
        /// <summary>
        /// Frame gradient start color. Must be set when FrameType is gradient.
        /// </summary>
        public System.Drawing.Color StartColor
        {
            get { return _startColor; }
            set { _startColor = value; }
        }

        /// <summary>
        /// Frame gradient end color. Must be set when FrameType is gradient.
        /// </summary>
        public System.Drawing.Color EndColor
        {
            get { return _endColor; }
            set { _endColor = value; }
        }
        
        /// <summary>
        /// Stripe image on the left side of the picture
        /// </summary>
        public Bitmap LeftImage
        {
            get { return _leftImage; }
            set { _leftImage = value; }
        }

        /// <summary>
        /// Stripe image on the right side of the picture
        /// </summary>
        public Bitmap RightImage
        {
            get { return _rightImage; }
            set { _rightImage = value; }
        }

        /// <summary>
        /// Stripe image above the picture
        /// </summary>
        public Bitmap TopImage
        {
            get { return _topImage; }
            set { _topImage = value; }
        }

        /// <summary>
        /// Stripe image below the picture
        /// </summary>
        public Bitmap BottomImage
        {
            get { return _bottomImage; }
            set { _bottomImage = value; }
        }

        /// <summary>
        /// Image for the left bottom corner
        /// </summary>
        public Bitmap LeftBottomImage
        {
            get { return _leftBottomImage; }
            set { _leftBottomImage = value; }
        }

        /// <summary>
        /// Image for the right bottom corner
        /// </summary>
        public Bitmap RightBottomImage
        {
            get { return _rightBottomImage; }
            set { _rightBottomImage = value; }
        }

        /// <summary>
        /// Image for the right top corner
        /// </summary>
        public Bitmap RightTopImage
        {
            get { return _rightTopImage; }
            set { _rightTopImage = value; }
        }

        /// <summary>
        /// Image for the left top corner
        /// </summary>
        public Bitmap LeftTopImage
        {
            get { return _leftTopImage; }
            set { _leftTopImage = value; }
        }

        /// <summary>
        /// When 0 and useColor is true - no top/bottom border exists.
        /// When 0 and useColor is false, frame will have the width of passed top border and bottom border images
        /// </summary>
        public int BorderHeight
        {
            get { return _borderHeight; }
            set { _borderHeight = value; }
        }

        /// <summary>
        /// When 0 and useColor is true - no left/right border exists.
        /// When 0 and useColor is false, frame will have the width of passed left border and righ border images
        /// </summary>
        public int BorderWidth
        {
            get { return _borderWidth; }
            set { _borderWidth = value; }
        }
        #endregion

        public Frame() { }

        #region Public Filter Methods
        public override void Apply(Bitmap b)
        {
            if (_frameType == FramingTypes.SimpleColor ||
                _frameType == FramingTypes.Gradient)
            {
                b = ProcessGradient(b);
            }
            else
            {
                b = ProcessFrame(b);
            }

            _b = b;
            _width = b.Width;
            _height = b.Height;
        }

        public override Bitmap Convert(Bitmap b)
        {
            Bitmap b2 = b.Clone() as Bitmap;
            Apply(b2);
            return b2;
        }
        #endregion

        #region We are using Simple color or Gradient background color
        private Bitmap ProcessGradient(Bitmap b)
        {
            if (_borderHeight == 0 &&
                _borderWidth == 0)
                return b;

            //Set up the new canvas
            Bitmap result = new Bitmap(b.Width + _borderWidth * 2, b.Height + _borderHeight * 2);
            Graphics g = Graphics.FromImage(result);

            // Set interpolation mode
            g.InterpolationMode = _interpolationMode;

            if (_frameType == FramingTypes.SimpleColor)
            {
                //Set background
                g.FillRectangle(new SolidBrush(BackGroundColor), 0, 0, result.Width, result.Height);
            }
            else if (_frameType == FramingTypes.Gradient)
            {
                LinearGradientBrush sideBrush = new LinearGradientBrush(
                    new Rectangle(0, 0, result.Width, result.Height),
                    _startColor, _endColor, LinearGradientMode.ForwardDiagonal);

                //Set background
                g.FillRectangle(sideBrush, 0, 0, result.Width, result.Height);
            }

            //FrontSide
            //Point rightBottom = new Point((int)(raw.Width * Math.Cos(alpha)) , raw.Height - (int)(raw.Width * Math.Sin(alpha)) + yAlign);
            //Point leftTop = new Point(_borderWidth, _borderHeight);
            //Point leftBottom = new Point(_borderWidth, b.Height + _borderHeight);
            //Point rightTop = new Point(_borderWidth + b.Width, _borderHeight);
            //g.DrawImage(b, new Point[] { leftTop, rightTop, leftBottom });
            g.DrawImage(b, new Point(_borderWidth, _borderHeight));

            return result;
        }
        #endregion

        #region We are using images for the frame
        private void ValidateAndConvert(Bitmap b)
        {
            #region Validate arguments
            if (_leftBottomImage == null) throw new ArgumentNullException("Left bottom image");
            if (_leftImage == null) throw new ArgumentNullException("Left image");
            if (_leftTopImage == null) throw new ArgumentNullException("Left top image");
            if (_rightBottomImage == null) throw new ArgumentNullException("Right bottom image");
            if (_rightImage == null) throw new ArgumentNullException("Right image");
            if (_rightTopImage == null) throw new ArgumentNullException("Right top image");
            if (_topImage == null) throw new ArgumentNullException("Top image");
            if (_bottomImage == null) throw new ArgumentNullException("Bottom image");
            #endregion

            #region Calculate thickness (align frame sides)
            int leftWidth = 0;
            int rightWidth = 0;
            int topHeight = 0;
            int bottomHeight = 0;

            if (_borderWidth > 0)
            {
                leftWidth = _leftTopImage.Width;
                if (_leftImage.Width > leftWidth) leftWidth = _leftImage.Width;
                if (_leftBottomImage.Width > leftWidth) leftWidth = _leftBottomImage.Width;

                rightWidth = _rightTopImage.Width;
                if (_rightImage.Width > rightWidth) rightWidth = _rightImage.Width;
                if (_rightBottomImage.Width > rightWidth) rightWidth = _rightBottomImage.Width;
            }
            else
            {
                leftWidth = rightWidth = _borderWidth;
            }

            if (_borderHeight > 0)
            {
                topHeight = _leftTopImage.Height;
                if (_topImage.Height > topHeight) topHeight = _topImage.Height;
                if (_rightTopImage.Height > topHeight) topHeight = _rightTopImage.Height;

                bottomHeight = _leftBottomImage.Height;
                if (_bottomImage.Height > bottomHeight) bottomHeight = _bottomImage.Height;
                if (_rightBottomImage.Height > bottomHeight) bottomHeight = _rightBottomImage.Height;
            }
            else
            {
                topHeight = bottomHeight = _borderHeight;
            }
            #endregion

            #region Modify images
            ResizeImage(_leftTopImage, leftWidth, topHeight);
            ResizeImage(_rightTopImage, rightWidth, topHeight);
            ResizeImage(_leftBottomImage, leftWidth, bottomHeight);
            ResizeImage(_rightBottomImage, rightWidth, bottomHeight);

            ResizeImage(_leftImage, leftWidth, b.Height);
            ResizeImage(_rightImage, rightWidth, b.Height);
            ResizeImage(_topImage, b.Width, topHeight);
            ResizeImage(_bottomImage, b.Width, bottomHeight);
            #endregion
        }

        #region ResizeImages
        private void ResizeImage(Bitmap b, int newWidth, int newHeight)
        {
            if (b.Width == newWidth && b.Height == newHeight)
                return;

            Resize filter = new Resize(newWidth, newHeight, false);
            filter.InterpolationMode = _interpolationMode;
            filter.Apply(b);
        }
        #endregion

        #region ProcessFrame
        private Bitmap ProcessFrame(Bitmap b)
        {
            ValidateAndConvert(b);

            //Set up the new canvas
            Bitmap result = new Bitmap(b.Width + _leftImage.Width + _rightImage.Width, 
                b.Height + _topImage.Height + _bottomImage.Height);
            Graphics g = Graphics.FromImage(result);

            // Set interpolation mode
            g.InterpolationMode = _interpolationMode;

            // Apply all 8 images
            g.DrawImage(_leftTopImage, new Point(0, 0));
            g.DrawImage(_topImage, new Point(_leftImage.Width, 0));
            g.DrawImage(_rightTopImage, new Point(_leftImage.Width + b.Width, 0));
            g.DrawImage(_rightImage, new Point(_leftImage.Width + b.Width, _topImage.Height));
            g.DrawImage(_rightBottomImage, new Point(_leftImage.Width + b.Width, _topImage.Height + b.Height));
            g.DrawImage(_bottomImage, new Point(_leftImage.Width, _topImage.Height + b.Height));
            g.DrawImage(_leftBottomImage, new Point(0, _topImage.Height + b.Height));
            g.DrawImage(_leftImage, new Point(0, _topImage.Height));

            // At long last - image itself
            g.DrawImage(b, new Point(_leftImage.Width, _topImage.Height));
            return result;
        }
        #endregion
        #endregion

        protected override void FilterImplementation()
        {
            throw new NotImplementedException("Low level mode is not implemented");
        }
    }
}
